<!DOCTYPE html>
<html lang="zh_cn">
<head>
          <title>来玩魔王的咚</title>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <meta charset="utf-8" />
        <!-- twitter card metadata -->
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="/images/mowang.png">
<meta name="twitter:site" content="">
<meta name="twitter:title" content="命令行参数（flag包）">
<meta name="twitter:description" content="<p>命令行参数：os.Args，flag基本使用，切片加参数，解析时间；自定义类型：接口说明，解析温度，打印默认值，解析自定义切片，简易的自定义版本；自定义命令参数容器：定制Usage，定制CommandLine，创建私有命令参数容器</p>">
        <!-- OG Tags -->
<meta property="og:url" content="/gopl-pkg-flag.html"/>
<meta property="og:title" content="来玩魔王的咚 | 命令行参数（flag包）" />
<meta property="og:description" content="<p>命令行参数：os.Args，flag基本使用，切片加参数，解析时间；自定义类型：接口说明，解析温度，打印默认值，解析自定义切片，简易的自定义版本；自定义命令参数容器：定制Usage，定制CommandLine，创建私有命令参数容器</p>" />
        <!-- favicon -->
        <link rel="icon" type="image/png" href="/images/mowang.png">
        <!-- moment.js for date formatting -->
        <script src="/theme/js/moment.js"></script>
        <!-- css -->
        <link rel="stylesheet" type="text/css" href="/theme/css/main.css" />
        <!-- 左边的menu，如果页面高度不够，就跟着滚动，否则文章分类显示不全 -->
        <link rel="stylesheet" type="text/css" href="/theme/css/mycss/menu.css" />
		<script>
			
                /*! grunt-grunticon Stylesheet Loader - v2.1.2 | https://github.com/filamentgroup/grunticon | (c) 2015 Scott Jehl, Filament Group, Inc. | MIT license. */
    
    (function(e){function t(t,n,r,o){"use strict";function a(){for(var e,n=0;u.length>n;n++)u[n].href&&u[n].href.indexOf(t)>-1&&(e=!0);e?i.media=r||"all":setTimeout(a)}var i=e.document.createElement("link"),l=n||e.document.getElementsByTagName("script")[0],u=e.document.styleSheets;return i.rel="stylesheet",i.href=t,i.media="only x",i.onload=o||null,l.parentNode.insertBefore(i,l),a(),i}var n=function(r,o){"use strict";if(r&&3===r.length){var a=e.navigator,i=e.Image,l=!(!document.createElementNS||!document.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect||!document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image","1.1")||e.opera&&-1===a.userAgent.indexOf("Chrome")||-1!==a.userAgent.indexOf("Series40")),u=new i;u.onerror=function(){n.method="png",n.href=r[2],t(r[2])},u.onload=function(){var e=1===u.width&&1===u.height,a=r[e&&l?0:e?1:2];n.method=e&&l?"svg":e?"datapng":"png",n.href=a,t(a,null,null,o)},u.src="",document.documentElement.className+=" grunticon"}};n.loadCSS=t,e.grunticon=n})(this);(function(e,t){"use strict";var n=t.document,r="grunticon:",o=function(e){if(n.attachEvent?"complete"===n.readyState:"loading"!==n.readyState)e();else{var t=!1;n.addEventListener("readystatechange",function(){t||(t=!0,e())},!1)}},a=function(e){return t.document.querySelector('link[href$="'+e+'"]')},c=function(e){var t,n,o,a,c,i,u={};if(t=e.sheet,!t)return u;n=t.cssRules?t.cssRules:t.rules;for(var l=0;n.length>l;l++)o=n[l].cssText,a=r+n[l].selectorText,c=o.split(");")[0].match(/US\-ASCII\,([^"']+)/),c&&c[1]&&(i=decodeURIComponent(c[1]),u[a]=i);return u},i=function(e){var t,o,a;o="data-grunticon-embed";for(var c in e)if(a=c.slice(r.length),t=n.querySelectorAll(a+"["+o+"]"),t.length)for(var i=0;t.length>i;i++)t[i].innerHTML=e[c],t[i].style.backgroundImage="none",t[i].removeAttribute(o);return t},u=function(t){"svg"===e.method&&o(function(){i(c(a(e.href))),"function"==typeof t&&t()})};e.embedIcons=i,e.getCSS=a,e.getIcons=c,e.ready=o,e.svgLoadedCallback=u,e.embedSVG=u})(grunticon,this);
                
                grunticon(["/theme/css/icons.data.svg.css", "/theme/css/icons.data.png.css", "/theme/css/icons.fallback.css"]);
            </script>
        <noscript><link href="/theme/css/icons.fallback.css" rel="stylesheet"></noscript>
        <!-- menu toggle javascript -->
        <script type="text/javascript">
            document.addEventListener("DOMContentLoaded", initMenu);
            
            function initMenu(){
                var menu = document.getElementById("menu");
                var menulink = document.getElementById("menu-link");
                menulink.addEventListener("click", function toggleMenu(){
                        window.event.preventDefault();
                        menulink.classList.toggle('active');
                        menu.classList.toggle('active');              
                    });
            };
        </script>
        <!-- 不蒜子 -->
        <script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>

    <meta name="description" content="<p>命令行参数：os.Args，flag基本使用，切片加参数，解析时间；自定义类型：接口说明，解析温度，打印默认值，解析自定义切片，简易的自定义版本；自定义命令参数容器：定制Usage，定制CommandLine，创建私有命令参数容器</p>" />

    <meta name="tags" content="gopl" />
    <meta name="tags" content="flag" />
    <meta name="tags" content="命令行参数" />
  <!-- 替换部分base的样式，看文章时，要再宽一点，右边有很多空间可以撑开 -->
  <link rel="stylesheet" type="text/css" href="/theme/css/mycss/article.css" />

</head>
<body>
    <div role="banner" id="masthead">
        <header>
            <a href="/"><img src="/images/mowang.png" alt="McManus Logo"></a>
                <h1>来玩魔王的咚@骑士救兵</h1>
            <a href="#menu" id="menu-link">more stuff</a>
            <nav id="menu">
                <ul>
                        <li><a href="/tags">tags</a></li>
                            <li><a href="/category/cloud.html">Cloud</a></li>
                            <li><a href="/category/docker.html">Docker</a></li>
                            <li class="active"><a href="/category/go.html">Go</a></li>
                            <li><a href="/category/linux.html">Linux</a></li>
                            <li><a href="/category/python.html">Python</a></li>
                            <li><a href="/category/xue-xi-bi-ji.html">学习笔记</a></li>
                            <li><a href="/category/yun-wei-zi-dong-hua.html">运维自动化</a></li>
                </ul>
            </nav>
        </header>
    </div>
        <div class="page" role="main">
  <div class="article" role="article">
    <article>
        <footer>
            <a name="top"></a>
            <p>
              <time datetime=" 2020-07-09 20:00:00+08:00">
                <script>document.write(moment('2020-07-09 20:00:00+08:00').format('LL'));</script>
              </time>
              ~
              <time datetime=" 2020-07-09 20:00:00+08:00">
                <script>document.write(moment('2020-07-09 20:00:00+08:00').format('LL'));</script>
              </time>
            </p>
        </footer>
        <header>
          <h2>
            命令行参数（flag包）
          </h2>
        </header>
      <div class="content">
         <div class="toc">
<ul>
<li><a href="#ming-ling-xing-can-shu">命令行参数</a><ul>
<li><a href="#osargs">os.Args</a></li>
<li><a href="#flag-ji-ben-shi-yong">flag 基本使用</a></li>
<li><a href="#qie-pian-jia-can-shu">切片加参数</a></li>
<li><a href="#jie-xi-shi-jian">解析时间</a></li>
</ul>
</li>
<li><a href="#zi-ding-yi-lei-xing">自定义类型</a><ul>
<li><a href="#jie-kou-shuo-ming">接口说明</a></li>
<li><a href="#zi-ding-yi-wen-du-jie-xi">自定义温度解析</a></li>
<li><a href="#da-yin-mo-ren-zhi">打印默认值</a></li>
<li><a href="#zi-ding-yi-qie-pian">自定义切片</a></li>
<li><a href="#jian-yi-de-zi-ding-yi-ban-ben">简易的自定义版本</a></li>
</ul>
</li>
<li><a href="#zi-ding-yi-ming-ling-can-shu-rong-qi">自定义命令参数容器</a><ul>
<li><a href="#ding-zhi-usage">定制 Usage</a></li>
<li><a href="#ding-zhi-commandline">定制 CommandLine</a></li>
<li><a href="#chuang-jian-si-you-ming-ling-can-shu-rong-qi">创建私有命令参数容器</a></li>
</ul>
</li>
</ul>
</div>
<h3 id="ming-ling-xing-can-shu"><a class="toclink" href="#ming-ling-xing-can-shu">命令行参数</a></h3>
<p>命令行参数可以直接通过 os.Args 获取，另外标准库的 flag 包专门用于接收和解除命令行参数。  </p>
<h4 id="osargs"><a class="toclink" href="#osargs">os.Args</a></h4>
<p>简单的只是从命令行获取一个或一组参数，可以直接使用 os.Args。<br>
下面的这种写法，无需进行判断，无论是否提供了命令行参数，或者提供了多个，都可以处理：</p>
<div class="highlight"><pre><span></span><code><span class="c1">// 把命令行参数，依次打印，每行一个</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="nx">_</span><span class="p">,</span><span class="w"> </span><span class="nx">s</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">os</span><span class="p">.</span><span class="nx">Args</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">s</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<h4 id="flag-ji-ben-shi-yong"><a class="toclink" href="#flag-ji-ben-shi-yong">flag 基本使用</a></h4>
<p>下面的例子使用了两种形式的调用方法：</p>
<div class="highlight"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w"></span>

<span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;flag&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;fmt&quot;</span><span class="w"></span>
<span class="p">)</span><span class="w"></span>

<span class="kd">var</span><span class="w"> </span><span class="nx">name</span><span class="w"> </span><span class="kt">string</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">init</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">flag</span><span class="p">.</span><span class="nx">StringVar</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">name</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;name&quot;</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;Adam&quot;</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;名字&quot;</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">var</span><span class="w"> </span><span class="nx">ageP</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">flag</span><span class="p">.</span><span class="nx">Int</span><span class="p">(</span><span class="s">&quot;age&quot;</span><span class="p">,</span><span class="w"> </span><span class="mi">18</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;年龄&quot;</span><span class="p">)</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">flag</span><span class="p">.</span><span class="nx">Parse</span><span class="p">()</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;%T %[1]v\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">name</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;%T %[1]v\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">ageP</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;%T %[1]v\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="o">*</span><span class="nx">ageP</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>第一种是直接把变量的指针传递给函数作为第一个参数，函数内部会对该变量进行赋值。这种形式必须写在一个函数体的内部。<br>
第二种是函数会把数据的指针作为函数的返回值返回，这种形式就是给变量赋值，不需要现在函数体内，不过拿到的返回值是指针。  </p>
<h4 id="qie-pian-jia-can-shu"><a class="toclink" href="#qie-pian-jia-can-shu">切片加参数</a></h4>
<p>这是上面两个实现的结合，可以提供一组 os.Args 的参数，另外还可以使用 flag 来进行参数设置。<br>
首先是不需要参数设置的情况。仅仅就是使用 flag 包提供的方法来代替使用 os.Args 的实现：</p>
<div class="highlight"><pre><span></span><code><span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">flag</span><span class="p">.</span><span class="nx">Parse</span><span class="p">()</span><span class="w"></span>
<span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="nx">_</span><span class="p">,</span><span class="w"> </span><span class="nx">arg</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">flag</span><span class="p">.</span><span class="nx">Args</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">arg</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>基本上没什么差别，不过引入 flag 包之后，就可以使用参数了，比如加上一个 -upper 参数，让输出全大写：</p>
<div class="highlight"><pre><span></span><code><span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="kd">var</span><span class="w"> </span><span class="nx">upper</span><span class="w"> </span><span class="kt">bool</span><span class="w"></span>
<span class="w">    </span><span class="nx">flag</span><span class="p">.</span><span class="nx">BoolVar</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">upper</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;upper&quot;</span><span class="p">,</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;是否大写&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">flag</span><span class="p">.</span><span class="nx">Parse</span><span class="p">()</span><span class="w"></span>
<span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="nx">_</span><span class="p">,</span><span class="w"> </span><span class="nx">arg</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">flag</span><span class="p">.</span><span class="nx">Args</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="nx">upper</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">strings</span><span class="p">.</span><span class="nx">ToUpper</span><span class="p">(</span><span class="nx">arg</span><span class="p">))</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">arg</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>自定义切片类型的实现下面会讲。不过像这样简单的使用，只有一个切片类型，也不需要使用自定义类型就可以方便的实现了：</p>
<div class="highlight"><pre><span></span><code>PS G:\Steed\Documents\Go\src\localdemo\flag&gt; go run main.go -upper hello hi bye
HELLO
HI
BYE
PS G:\Steed\Documents\Go\src\localdemo\flag&gt;
</code></pre></div>

<p>命令行参数必须放在前面，把不需要解析的参数全部放在最后。  </p>
<h4 id="jie-xi-shi-jian"><a class="toclink" href="#jie-xi-shi-jian">解析时间</a></h4>
<p>时间长度类的命令行标志应用广泛，这个功能内置到了 flag 包中。<br>
先看看源码中的示例，之后在自定义命令行标志的时候也能有个参考。下面的示例，实现了暂停指定时间的功能：</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span><span class="w"> </span><span class="nx">period</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">flag</span><span class="p">.</span><span class="nx">Duration</span><span class="p">(</span><span class="s">&quot;period&quot;</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="o">*</span><span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;sleep period&quot;</span><span class="p">)</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">flag</span><span class="p">.</span><span class="nx">Parse</span><span class="p">()</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;Sleeping for %v...&quot;</span><span class="p">,</span><span class="w"> </span><span class="o">*</span><span class="nx">period</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">time</span><span class="p">.</span><span class="nx">Sleep</span><span class="p">(</span><span class="o">*</span><span class="nx">period</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">()</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>默认是1秒，但是可以通过参数来控制。flag.Duration函数创建了一个 *time.Duration 类型的标志变量，并且允许用户用一种友好的方式来指定时长。就是用 String 方法对应的记录方法。这种对称的设计提供了一个良好的用户接口。</p>
<div class="highlight"><pre><span></span><code>PS H:\Go\src\gopl\ch7\sleep&gt; go run main.go -period 3s
Sleeping for 3s...
PS H:\Go\src\gopl\ch7\sleep&gt; go run main.go -period 1m
Sleeping for 1m0s...
PS H:\Go\src\gopl\ch7\sleep&gt; go run main.go -period 1.5h
Sleeping for 1h30m0s...
</code></pre></div>

<h3 id="zi-ding-yi-lei-xing"><a class="toclink" href="#zi-ding-yi-lei-xing">自定义类型</a></h3>
<p>更多的情况下，是需要自己实现接口来进行自定义的。  </p>
<h4 id="jie-kou-shuo-ming"><a class="toclink" href="#jie-kou-shuo-ming">接口说明</a></h4>
<p>支持自定义类型，需要定义一个满足 flag.Value 接口的类型：</p>
<div class="highlight"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">flag</span><span class="w"></span>

<span class="c1">// Value 接口代表了存储在标志内的值</span><span class="w"></span>
<span class="kd">type</span><span class="w"> </span><span class="nx">Value</span><span class="w"> </span><span class="kd">interface</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">String</span><span class="p">()</span><span class="w"> </span><span class="kt">string</span><span class="w"></span>
<span class="w">    </span><span class="nx">Set</span><span class="p">(</span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>String 方法用于格式化标志对应的值，可用于输出命令行帮助消息。<br>
Set 方法解析了传入的字符串参数并更新标志值。可以认为 Set 方法是 String 方法的逆操作，这两个方法使用同样的记法规格是一个很好的实践。  </p>
<h4 id="zi-ding-yi-wen-du-jie-xi"><a class="toclink" href="#zi-ding-yi-wen-du-jie-xi">自定义温度解析</a></h4>
<p>下面定义 celsiusFlag 类型来允许在参数中使用摄氏温度或华氏温度。因为 Celsius 类型原本就已经实现了 String 方法，这里把 Celsius 内嵌到了 celsiusFlag 结构体中，这样结构体有就有了 String 方法（<em>外围结构体类型不仅获取了匿名成员的内部变量，还有相关方法</em>）。<br>
所以为了满足接口，只须再定一个 Set 方法：</p>
<div class="highlight"><pre><span></span><code><span class="kd">type</span><span class="w"> </span><span class="nx">Celsius</span><span class="w"> </span><span class="kt">float64</span><span class="w"></span>
<span class="kd">type</span><span class="w"> </span><span class="nx">Fahrenheit</span><span class="w"> </span><span class="kt">float64</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">CToF</span><span class="p">(</span><span class="nx">c</span><span class="w"> </span><span class="nx">Celsius</span><span class="p">)</span><span class="w"> </span><span class="nx">Fahrenheit</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">Fahrenheit</span><span class="p">(</span><span class="nx">c</span><span class="o">*</span><span class="mf">9.0</span><span class="o">/</span><span class="mf">5.0</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mf">32.0</span><span class="p">)</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="nx">FToC</span><span class="p">(</span><span class="nx">f</span><span class="w"> </span><span class="nx">Fahrenheit</span><span class="p">)</span><span class="w"> </span><span class="nx">Celsius</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">Celsius</span><span class="p">((</span><span class="nx">f</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mf">32.0</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="mf">5.0</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mf">9.0</span><span class="p">)</span><span class="w"> </span><span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">c</span><span class="w"> </span><span class="nx">Celsius</span><span class="p">)</span><span class="w"> </span><span class="nx">String</span><span class="p">()</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Sprintf</span><span class="p">(</span><span class="s">&quot;%g°C&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">c</span><span class="p">)</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="c1">// 上面这些都是之前在别处定义过的内容，是可以作为包引出过来的</span><span class="w"></span>
<span class="c1">// 为了说明清楚，就单独把需要用到的部分复制过来</span><span class="w"></span>

<span class="c1">// *celsiusFlag 满足 flag.Vulue 接口</span><span class="w"></span>
<span class="kd">type</span><span class="w"> </span><span class="nx">celsiusFlag</span><span class="w"> </span><span class="kd">struct</span><span class="p">{</span><span class="w"> </span><span class="nx">Celsius</span><span class="w"> </span><span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">f</span><span class="w"> </span><span class="o">*</span><span class="nx">celsiusFlag</span><span class="p">)</span><span class="w"> </span><span class="nx">Set</span><span class="p">(</span><span class="nx">s</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="kd">var</span><span class="w"> </span><span class="nx">unit</span><span class="w"> </span><span class="kt">string</span><span class="w"></span>
<span class="w">    </span><span class="kd">var</span><span class="w"> </span><span class="nx">value</span><span class="w"> </span><span class="kt">float64</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Sscanf</span><span class="p">(</span><span class="nx">s</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;%f%s&quot;</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">value</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">unit</span><span class="p">)</span><span class="w"> </span><span class="c1">// 无须检查错误</span><span class="w"></span>
<span class="w">    </span><span class="k">switch</span><span class="w"> </span><span class="nx">unit</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="s">&quot;C&quot;</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;°C&quot;</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="nx">f</span><span class="p">.</span><span class="nx">Celsius</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">Celsius</span><span class="p">(</span><span class="nx">value</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="s">&quot;F&quot;</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;°F&quot;</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="nx">f</span><span class="p">.</span><span class="nx">Celsius</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">FToC</span><span class="p">(</span><span class="nx">Fahrenheit</span><span class="p">(</span><span class="nx">value</span><span class="p">))</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;invalid temperature %q&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">s</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>fmt.Sscanf 函数用于从输入 s 解析一个浮点值和一个字符串。通常是需要检查错误的，但是这里如果出错，后面的 switch 里的条件也是无法满足的，是可以通过switch之后的错误处理来一并进行处理的。<br>
这里还需要写一个 CelsiusFlag 函数来封装上面的逻辑。这个函数返回了一个 Celsius 的指针，它指向嵌入在 celsiusFlag 变量 f 中的一个字段。Celsius 字段在标志处理过程中会发生变化（经由Set方法）。<br>
调用 Var 方法可以把这个标志加入到程序的命令行标记集合中，即全局变量 flag.CommandLine。<em>如果一个程序有非常复杂的命令行接口，那么单个全局变量就不够用了，需要多个类似的变量来支撑。最后一节“创建私有命令参数容器”会做简单的展开，不过也没有实现到这个程度。</em> <br>
调用 Var 方法是会把 *celsiusFlag 实参赋给 flag.Value 形参，编译器会在此时检查 *celsiusFlag 类型是否有 flag.Value 所必需的方法：</p>
<div class="highlight"><pre><span></span><code><span class="kd">func</span><span class="w"> </span><span class="nx">CelsiusFlag</span><span class="p">(</span><span class="nx">name</span><span class="w"> </span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">value</span><span class="w"> </span><span class="nx">Celsius</span><span class="p">,</span><span class="w"> </span><span class="nx">usage</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="nx">Celsius</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">f</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">celsiusFlag</span><span class="p">{</span><span class="nx">value</span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="nx">flag</span><span class="p">.</span><span class="nx">CommandLine</span><span class="p">.</span><span class="nx">Var</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">f</span><span class="p">,</span><span class="w"> </span><span class="nx">name</span><span class="p">,</span><span class="w"> </span><span class="nx">usage</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">f</span><span class="p">.</span><span class="nx">Celsius</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>现在就可以在程序中使用这个标志了，使用代码如下：</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span><span class="w"> </span><span class="nx">temp</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">CelsiusFlag</span><span class="p">(</span><span class="s">&quot;temp&quot;</span><span class="p">,</span><span class="w"> </span><span class="mf">20.0</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;温度&quot;</span><span class="p">)</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">flag</span><span class="p">.</span><span class="nx">Parse</span><span class="p">()</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="o">*</span><span class="nx">temp</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>接下来还可以把上面的例子简单改一下，不用结构体了，而是换成变量的别名，这样就需要额外再实现一个String方法，完整的代码如下：</p>
<div class="highlight"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w"></span>

<span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;flag&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;fmt&quot;</span><span class="w"></span>
<span class="p">)</span><span class="w"></span>

<span class="kd">type</span><span class="w"> </span><span class="nx">Celsius</span><span class="w"> </span><span class="kt">float64</span><span class="w"></span>
<span class="kd">type</span><span class="w"> </span><span class="nx">Fahrenheit</span><span class="w"> </span><span class="kt">float64</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">CToF</span><span class="p">(</span><span class="nx">c</span><span class="w"> </span><span class="nx">Celsius</span><span class="p">)</span><span class="w"> </span><span class="nx">Fahrenheit</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">Fahrenheit</span><span class="p">(</span><span class="nx">c</span><span class="o">*</span><span class="mf">9.0</span><span class="o">/</span><span class="mf">5.0</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mf">32.0</span><span class="p">)</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="nx">FToC</span><span class="p">(</span><span class="nx">f</span><span class="w"> </span><span class="nx">Fahrenheit</span><span class="p">)</span><span class="w"> </span><span class="nx">Celsius</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">Celsius</span><span class="p">((</span><span class="nx">f</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mf">32.0</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="mf">5.0</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mf">9.0</span><span class="p">)</span><span class="w"> </span><span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">c</span><span class="w"> </span><span class="nx">Celsius</span><span class="p">)</span><span class="w"> </span><span class="nx">String</span><span class="p">()</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Sprintf</span><span class="p">(</span><span class="s">&quot;%g°C&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">c</span><span class="p">)</span><span class="w"> </span><span class="p">}</span><span class="w"></span>

<span class="c1">// 上面这些都是之前定义过的内容，是可以作为包引出过来的</span><span class="w"></span>
<span class="c1">// 为了说明清楚，就单独把需要用到的部分复制过来</span><span class="w"></span>

<span class="c1">// *celsiusValue 满足 flag.Vulue 接口</span><span class="w"></span>
<span class="c1">// 同一个包不必这么麻烦，直接定义 Celsius 类型即可。这里假设是从别的包引入的类型</span><span class="w"></span>
<span class="kd">type</span><span class="w"> </span><span class="nx">celsiusValue</span><span class="w"> </span><span class="nx">Celsius</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">c</span><span class="w"> </span><span class="o">*</span><span class="nx">celsiusValue</span><span class="p">)</span><span class="w"> </span><span class="nx">String</span><span class="p">()</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Sprintf</span><span class="p">(</span><span class="s">&quot;%.2f°C&quot;</span><span class="p">,</span><span class="w"> </span><span class="o">*</span><span class="nx">c</span><span class="p">)</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="c1">// func (c *celsiusValue) String() string { return (*Celsius)(c).String() }</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">c</span><span class="w"> </span><span class="o">*</span><span class="nx">celsiusValue</span><span class="p">)</span><span class="w"> </span><span class="nx">Set</span><span class="p">(</span><span class="nx">s</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="kd">var</span><span class="w"> </span><span class="nx">unit</span><span class="w"> </span><span class="kt">string</span><span class="w"></span>
<span class="w">    </span><span class="kd">var</span><span class="w"> </span><span class="nx">value</span><span class="w"> </span><span class="kt">float64</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Sscanf</span><span class="p">(</span><span class="nx">s</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;%f%s&quot;</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">value</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">unit</span><span class="p">)</span><span class="w"> </span><span class="c1">// 无须检查错误</span><span class="w"></span>
<span class="w">    </span><span class="k">switch</span><span class="w"> </span><span class="nx">unit</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="s">&quot;C&quot;</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;°C&quot;</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="o">*</span><span class="nx">c</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">celsiusValue</span><span class="p">(</span><span class="nx">value</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="s">&quot;F&quot;</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;°F&quot;</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="o">*</span><span class="nx">c</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">celsiusValue</span><span class="p">(</span><span class="nx">FToC</span><span class="p">(</span><span class="nx">Fahrenheit</span><span class="p">(</span><span class="nx">value</span><span class="p">)))</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;invalid temperature %q&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">s</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">CelsiusFlag</span><span class="p">(</span><span class="nx">name</span><span class="w"> </span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">value</span><span class="w"> </span><span class="nx">Celsius</span><span class="p">,</span><span class="w"> </span><span class="nx">usage</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="nx">Celsius</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">p</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nb">new</span><span class="p">(</span><span class="nx">Celsius</span><span class="p">)</span><span class="w"> </span><span class="c1">// value 是传值进来的，取不到地址，new一个内存空间，存放value的值</span><span class="w"></span>
<span class="w">    </span><span class="o">*</span><span class="nx">p</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">value</span><span class="w"></span>
<span class="w">    </span><span class="nx">flag</span><span class="p">.</span><span class="nx">CommandLine</span><span class="p">.</span><span class="nx">Var</span><span class="p">((</span><span class="o">*</span><span class="nx">celsiusValue</span><span class="p">)(</span><span class="nx">p</span><span class="p">),</span><span class="w"> </span><span class="nx">name</span><span class="p">,</span><span class="w"> </span><span class="nx">usage</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">p</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">tempP</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">CelsiusFlag</span><span class="p">(</span><span class="s">&quot;temp&quot;</span><span class="p">,</span><span class="w"> </span><span class="mf">36.7</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;温度&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">flag</span><span class="p">.</span><span class="nx">Parse</span><span class="p">()</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;%T, %[1]v\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">tempP</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<h4 id="da-yin-mo-ren-zhi"><a class="toclink" href="#da-yin-mo-ren-zhi">打印默认值</a></h4>
<p>使用上面最后一个例子，打印帮助，查看默认值的提示：</p>
<div class="highlight"><pre><span></span><code>PS G:\Steed\Documents\Go\src\gopl\output\flag\tempconv2&gt; go run main.go -h
Usage of C:\Users\Steed\AppData\Local\Temp\go-build840446178\b001\exe\main.exe:
  -temp value
        温度 (default 36.70°C)
exit status 2
PS G:\Steed\Documents\Go\src\gopl\output\flag\tempconv2&gt; go run main.go -temp 36.7C
*main.Celsius, 36.7°C
PS G:\Steed\Documents\Go\src\gopl\output\flag\tempconv2&gt;
</code></pre></div>

<p>默认值打印的格式和打印只的格式是有区别的，这是因为类型不同，调用了不同的 String 方法。<br>
这里默认值显示的格式是根据接口类型的String方法定义的，在这里就是 *celsiusValue 类型的String方法。而后面打印的是 Celsius 类型，使用的是 Celsius 类型的 String 方法。这里定义了两个String方法，但是打印的效果又不同，显示不统一，这样的做法不够好。这里可以看出两个问题：</p>
<ol>
<li>最初，使用结构体匿名封装的形式，避免了重复定义 String 方法。这样就保证了自定义的结构体类型 celsiusFlag 的String方法就是原本的 Celsius 类型的String方法。</li>
<li>帮助消息中打印的默认值，实际是打印自定义类型的值。而自定义类型只在flag包中有用，解析完成后使用的都是原本的类型，这里就是 Celsius 类型。这两个类型的String方法最好能保持一致。</li>
</ol>
<p>所以，使用结构体封装应该是一种不错的实现方式。不过flag包中的 time.Duration 类型用的就是类型别名来实现的：</p>
<div class="highlight"><pre><span></span><code><span class="kd">type</span><span class="w"> </span><span class="nx">durationValue</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Duration</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">d</span><span class="w"> </span><span class="o">*</span><span class="nx">durationValue</span><span class="p">)</span><span class="w"> </span><span class="nx">Set</span><span class="p">(</span><span class="nx">s</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">v</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">ParseDuration</span><span class="p">(</span><span class="nx">s</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="o">*</span><span class="nx">d</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">durationValue</span><span class="p">(</span><span class="nx">v</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">err</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">d</span><span class="w"> </span><span class="o">*</span><span class="nx">durationValue</span><span class="p">)</span><span class="w"> </span><span class="nx">String</span><span class="p">()</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="nx">time</span><span class="p">.</span><span class="nx">Duration</span><span class="p">)(</span><span class="nx">d</span><span class="p">).</span><span class="nx">String</span><span class="p">()</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>上面是源码中的部分代码，可以看出这里保持一致的方法是进行类型转换后，调用原来类型的String方法。可能原本定义的是值类型的String方法，也可能直接就是定义了指针类型的String方法，不过指针类型的方法包括了所有值类型的方法，所以这里不必关系原本类型的方法具体是指针方法还是值方法。<br>
所以最后一个示例中的String方法也可以做同样的修改：</p>
<div class="highlight"><pre><span></span><code><span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">c</span><span class="w"> </span><span class="o">*</span><span class="nx">celsiusValue</span><span class="p">)</span><span class="w"> </span><span class="nx">String</span><span class="p">()</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="nx">Celsius</span><span class="p">)(</span><span class="nx">f</span><span class="p">).</span><span class="nx">String</span><span class="p">()</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
</code></pre></div>

<h4 id="zi-ding-yi-qie-pian"><a class="toclink" href="#zi-ding-yi-qie-pian">自定义切片</a></h4>
<p>以字符串切片为例，这里有两种实现的思路。一种是直接提供一个字符串，然后做分隔得到切片：</p>
<div class="highlight"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w"></span>

<span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;flag&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;fmt&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;strings&quot;</span><span class="w"></span>
<span class="p">)</span><span class="w"></span>

<span class="kd">type</span><span class="w"> </span><span class="nx">fullName</span><span class="w"> </span><span class="p">[]</span><span class="kt">string</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">v</span><span class="w"> </span><span class="o">*</span><span class="nx">fullName</span><span class="p">)</span><span class="w"> </span><span class="nx">String</span><span class="p">()</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">r</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="p">[]</span><span class="kt">string</span><span class="p">{}</span><span class="w"></span>
<span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="nx">_</span><span class="p">,</span><span class="w"> </span><span class="nx">s</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="o">*</span><span class="nx">v</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">r</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">append</span><span class="p">(</span><span class="nx">r</span><span class="p">,</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Sprintf</span><span class="p">(</span><span class="s">&quot;%q&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">s</span><span class="p">))</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">strings</span><span class="p">.</span><span class="nx">Join</span><span class="p">(</span><span class="nx">r</span><span class="p">,</span><span class="w"> </span><span class="s">&quot; &quot;</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">v</span><span class="w"> </span><span class="o">*</span><span class="nx">fullName</span><span class="p">)</span><span class="w"> </span><span class="nx">Set</span><span class="p">(</span><span class="nx">s</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="o">*</span><span class="nx">v</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="kc">nil</span><span class="w"></span>
<span class="w">    </span><span class="c1">// strings.Fields 可以区分连续的空格</span><span class="w"></span>
<span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="nx">_</span><span class="p">,</span><span class="w"> </span><span class="nx">str</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">strings</span><span class="p">.</span><span class="nx">Fields</span><span class="p">(</span><span class="nx">s</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="o">*</span><span class="nx">v</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">append</span><span class="p">(</span><span class="o">*</span><span class="nx">v</span><span class="p">,</span><span class="w"> </span><span class="nx">str</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">FullName</span><span class="p">(</span><span class="nx">name</span><span class="w"> </span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">value</span><span class="w"> </span><span class="p">[]</span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">usage</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="p">[]</span><span class="kt">string</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">p</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nb">new</span><span class="p">([]</span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="c1">// value 是传值进来的，取不到地址，new一个内存空间，存放value的值</span><span class="w"></span>
<span class="w">    </span><span class="o">*</span><span class="nx">p</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">value</span><span class="w"></span>
<span class="w">    </span><span class="nx">flag</span><span class="p">.</span><span class="nx">CommandLine</span><span class="p">.</span><span class="nx">Var</span><span class="p">((</span><span class="o">*</span><span class="nx">fullName</span><span class="p">)(</span><span class="nx">p</span><span class="p">),</span><span class="w"> </span><span class="nx">name</span><span class="p">,</span><span class="w"> </span><span class="nx">usage</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">p</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">s</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">FullName</span><span class="p">(</span><span class="s">&quot;name&quot;</span><span class="p">,</span><span class="w"> </span><span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">&quot;Karl&quot;</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;Lichter&quot;</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;Von&quot;</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;Randoll&quot;</span><span class="p">},</span><span class="w"> </span><span class="s">&quot;全名&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">flag</span><span class="p">.</span><span class="nx">Parse</span><span class="p">()</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;% q\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="o">*</span><span class="nx">s</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>这里就不管 String 方法和 Set 方法展示规格的一致了，String方法采用 %q 的输出形式可以更好的把每一个元素清楚的展示出来。  </p>
<p>还有一种方式是，可以多次调用同一个参数，每一次调用，就添加一个元素：</p>
<div class="highlight"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w"></span>

<span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;flag&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;fmt&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;strings&quot;</span><span class="w"></span>
<span class="p">)</span><span class="w"></span>

<span class="kd">type</span><span class="w"> </span><span class="nx">urls</span><span class="w"> </span><span class="p">[]</span><span class="kt">string</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">v</span><span class="w"> </span><span class="o">*</span><span class="nx">urls</span><span class="p">)</span><span class="w"> </span><span class="nx">String</span><span class="p">()</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">r</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="p">[]</span><span class="kt">string</span><span class="p">{}</span><span class="w"></span>
<span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="nx">_</span><span class="p">,</span><span class="w"> </span><span class="nx">s</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="o">*</span><span class="nx">v</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">r</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">append</span><span class="p">(</span><span class="nx">r</span><span class="p">,</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Sprintf</span><span class="p">(</span><span class="s">&quot;%q&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">s</span><span class="p">))</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">strings</span><span class="p">.</span><span class="nx">Join</span><span class="p">(</span><span class="nx">r</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;, &quot;</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">v</span><span class="w"> </span><span class="o">*</span><span class="nx">urls</span><span class="p">)</span><span class="w"> </span><span class="nx">Set</span><span class="p">(</span><span class="nx">s</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="c1">// *v = nil // 不能再清空原有的记录了</span><span class="w"></span>
<span class="w">    </span><span class="c1">// strings.Fields 可以区分连续的空格</span><span class="w"></span>
<span class="w">    </span><span class="o">*</span><span class="nx">v</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">append</span><span class="p">(</span><span class="o">*</span><span class="nx">v</span><span class="p">,</span><span class="w"> </span><span class="nx">s</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">Urls</span><span class="p">(</span><span class="nx">name</span><span class="w"> </span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">value</span><span class="w"> </span><span class="p">[]</span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">usage</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="p">[]</span><span class="kt">string</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">p</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nb">new</span><span class="p">([]</span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="c1">// value 是传值进来的，取不到地址，new一个内存空间，存放value的值</span><span class="w"></span>
<span class="w">    </span><span class="o">*</span><span class="nx">p</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">value</span><span class="w"></span>
<span class="w">    </span><span class="nx">flag</span><span class="p">.</span><span class="nx">CommandLine</span><span class="p">.</span><span class="nx">Var</span><span class="p">((</span><span class="o">*</span><span class="nx">urls</span><span class="p">)(</span><span class="nx">p</span><span class="p">),</span><span class="w"> </span><span class="nx">name</span><span class="p">,</span><span class="w"> </span><span class="nx">usage</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">p</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">s</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">Urls</span><span class="p">(</span><span class="s">&quot;url&quot;</span><span class="p">,</span><span class="w"> </span><span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">&quot;baidu.com&quot;</span><span class="p">},</span><span class="w"> </span><span class="s">&quot;域名&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">flag</span><span class="p">.</span><span class="nx">Parse</span><span class="p">()</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;% q\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="o">*</span><span class="nx">s</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>由于每出现一个参数，都会调用一次 Set 方法，所以只要在 Set 里对切片进行append就可以了。不过这也带来一个问题，就是默认值无法被覆盖掉：</p>
<div class="highlight"><pre><span></span><code>PS G:\Steed\Documents\Go\src\gopl\output\flag\urls&gt; go run main.go -h
Usage of C:\Users\Steed\AppData\Local\Temp\go-build727433198\b001\exe\main.exe:
  -url value
        域名 (default &quot;baidu.com&quot;)
exit status 2
PS G:\Steed\Documents\Go\src\gopl\output\flag\urls&gt; go run main.go
[&quot;baidu.com&quot;]
PS G:\Steed\Documents\Go\src\gopl\output\flag\urls&gt; go run main.go -url shuxun.net -url 51cto.com
[&quot;baidu.com&quot; &quot;shuxun.net&quot; &quot;51cto.com&quot;]
PS G:\Steed\Documents\Go\src\gopl\output\flag\urls&gt;
</code></pre></div>

<p>下面这个版本的Set方法引入了一个全局变量，可以改进上面的问题：</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span><span class="w"> </span><span class="nx">isNew</span><span class="w"> </span><span class="kt">bool</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">v</span><span class="w"> </span><span class="o">*</span><span class="nx">urls</span><span class="p">)</span><span class="w"> </span><span class="nx">Set</span><span class="p">(</span><span class="nx">s</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="p">!</span><span class="nx">isNew</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="o">*</span><span class="nx">v</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="kc">nil</span><span class="w"></span>
<span class="w">        </span><span class="nx">isNew</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="kc">true</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="o">*</span><span class="nx">v</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">append</span><span class="p">(</span><span class="o">*</span><span class="nx">v</span><span class="p">,</span><span class="w"> </span><span class="nx">s</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>这里是一个方法，无法改成闭包。最好的做法就是将这个变量和原本的字符串切片封装为一个结构体：</p>
<div class="highlight"><pre><span></span><code><span class="kd">type</span><span class="w"> </span><span class="nx">urls</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">data</span><span class="w">  </span><span class="p">[]</span><span class="kt">string</span><span class="w"></span>
<span class="w">    </span><span class="nx">isNew</span><span class="w"> </span><span class="kt">bool</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>剩下的修改，参考之前自定义温度解析的实现就差不多了。  </p>
<h4 id="jian-yi-de-zi-ding-yi-ban-ben"><a class="toclink" href="#jian-yi-de-zi-ding-yi-ban-ben">简易的自定义版本</a></h4>
<p>要实现自定义类型，只需要实现接口就可以了。不过上面的例子中都额外写了一个函数，用于返回自定义类型的指针，并且还设置了默认值。这个方法内部也是调用 Var 方法。<br>
这里可以直接使用 flag 包里的 Var 函数调用全局的Var方法：</p>
<div class="highlight"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w"></span>

<span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;flag&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;fmt&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;strings&quot;</span><span class="w"></span>
<span class="p">)</span><span class="w"></span>

<span class="kd">type</span><span class="w"> </span><span class="nx">urls</span><span class="w"> </span><span class="p">[]</span><span class="kt">string</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">v</span><span class="w"> </span><span class="o">*</span><span class="nx">urls</span><span class="p">)</span><span class="w"> </span><span class="nx">String</span><span class="p">()</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="c1">// *v = []string{&quot;baidu.com&quot;} // 通过指针改变初始值</span><span class="w"></span>
<span class="w">    </span><span class="nx">r</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="p">[]</span><span class="kt">string</span><span class="p">{}</span><span class="w"></span>
<span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="nx">_</span><span class="p">,</span><span class="w"> </span><span class="nx">s</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="o">*</span><span class="nx">v</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">r</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">append</span><span class="p">(</span><span class="nx">r</span><span class="p">,</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Sprintf</span><span class="p">(</span><span class="s">&quot;%q&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">s</span><span class="p">))</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">strings</span><span class="p">.</span><span class="nx">Join</span><span class="p">(</span><span class="nx">r</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;, &quot;</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">var</span><span class="w"> </span><span class="nx">isNew</span><span class="w"> </span><span class="kt">bool</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">v</span><span class="w"> </span><span class="o">*</span><span class="nx">urls</span><span class="p">)</span><span class="w"> </span><span class="nx">Set</span><span class="p">(</span><span class="nx">s</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="p">!</span><span class="nx">isNew</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="o">*</span><span class="nx">v</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="kc">nil</span><span class="w"></span>
<span class="w">        </span><span class="nx">isNew</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="kc">true</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="o">*</span><span class="nx">v</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">append</span><span class="p">(</span><span class="o">*</span><span class="nx">v</span><span class="p">,</span><span class="w"> </span><span class="nx">s</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="kd">var</span><span class="w"> </span><span class="nx">value</span><span class="w"> </span><span class="nx">urls</span><span class="w"></span>
<span class="w">    </span><span class="c1">// value = append(value, &quot;baidu.com&quot;) // 传递给Var函数前就设定好初始值</span><span class="w"></span>
<span class="w">    </span><span class="nx">flag</span><span class="p">.</span><span class="nx">Var</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">value</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;url&quot;</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;域名&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">flag</span><span class="p">.</span><span class="nx">Parse</span><span class="p">()</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;%T % [1]q\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">value</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">s</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="p">[]</span><span class="nb">string</span><span class="p">(</span><span class="nx">value</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;%T % [1]q\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">s</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>这里提供了两个设置初始值的方法，示例中都注释掉了。<br>
String 方法由于内部是获得指针的，所以可以对变量进行修改。并且该方法调用的时机是在解析开始时只调用一次。所以在 String 方法里设置默认值是可行的。不过无法在打印帮助的时候把默认值打印出来。<em>不需要这么做，但是正好可以对String方法有进一步的了解，还有就是这里利用指针修改参数原值的思路。</em><br>
另外，由于 Var 函数需要接收一个变量，所以在定义变量的时候，就可以赋一个初始值。并且在打印帮助的时候是可以把这个初始值打印出来的。<br>
不过简易版本最大的问题就是 Var 函数接收和返回的值都是 Value 接口类型。所以在使用之前，需要对返回值做一次类型转换。而设置初始值也是对 Value 接口类型的值进行设置。主要问题就是对外暴露了 Value 类型。现在调用者必须知道并且使用 Value 类型，对 Value 类型进行处理，这样就不是很友好。而之前的示例中，调用方（就是main函数中的那些代码）是完全可以忽略 Value 的存在的。  </p>
<p><strong>小结：</strong>这一小段主要是为了说明，之前示例中额外定义的函数是非常好的做法，封装了 flag 内部接口的细节。经过这个函数封装后再提供给用户使用，用户就可以完全忽略 flag.Value 这个接口而直接操作真正需要的类型了。这个函数的作用就是封装接口的所有细节，调用者只需要关注真正需要的操作的类型。  </p>
<h3 id="zi-ding-yi-ming-ling-can-shu-rong-qi"><a class="toclink" href="#zi-ding-yi-ming-ling-can-shu-rong-qi">自定义命令参数容器</a></h3>
<p>接下来就是通过包提供的方法行进一步的自定制。以下3小节是一层一层更加接近底层的调用，做更加深入的定制。   </p>
<h4 id="ding-zhi-usage"><a class="toclink" href="#ding-zhi-usage">定制 Usage</a></h4>
<p>回到最基本的使用，打印一下帮助消息可以得到以下的内容：</p>
<div class="highlight"><pre><span></span><code>PS H:\Go\src\gopl\output\flag\beginning&gt; go run main.go -h
Usage of C:\Users\Steed\AppData\Local\Temp\go-build926710106\b001\exe\main.exe:
  -age int
        年龄 (default 18)
  -name string
        名字 (default &quot;Adam&quot;)
exit status 2
PS H:\Go\src\gopl\output\flag\beginning&gt;
</code></pre></div>

<p>这里关注第一行，在 Usage of 后面是一长串的路径，这个是go run命令在构建上述命令源码文件时临时生成的可执行文件的完整路径。如果是编译之后再执行，就是可执行文件的相对路径，就没那么难看了。<br>
这一行的内容也是可以自定制的，但是首先来看看源码里的实现：</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span><span class="w"> </span><span class="nx">Usage</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="kd">func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Fprintf</span><span class="p">(</span><span class="nx">CommandLine</span><span class="p">.</span><span class="nx">Output</span><span class="p">(),</span><span class="w"> </span><span class="s">&quot;Usage of %s:\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">os</span><span class="p">.</span><span class="nx">Args</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span><span class="w"></span>
<span class="w">    </span><span class="nx">PrintDefaults</span><span class="p">()</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">f</span><span class="w"> </span><span class="o">*</span><span class="nx">FlagSet</span><span class="p">)</span><span class="w"> </span><span class="nx">Output</span><span class="p">()</span><span class="w"> </span><span class="nx">io</span><span class="p">.</span><span class="nx">Writer</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">f</span><span class="p">.</span><span class="nx">output</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">f</span><span class="p">.</span><span class="nx">output</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>看上面的代码就清楚了，输出的内容就是执行的命令本身 os.Args[0]。就会输出的位置默认就是标准错误 os.Stderr。<br>
这个 Usage 是可导出的变量，值是一个匿名函数，只要重新为 Usage 赋一个新值就可以完成内容的自定制：</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span><span class="w"> </span><span class="nx">name</span><span class="w"> </span><span class="kt">string</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">init</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">flag</span><span class="p">.</span><span class="nx">StringVar</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">name</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;name&quot;</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;Adam&quot;</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;名字&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">flag</span><span class="p">.</span><span class="nx">Usage</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="kd">func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Fprintln</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;请指定名字和年龄：&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="nx">flag</span><span class="p">.</span><span class="nx">PrintDefaults</span><span class="p">()</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">var</span><span class="w"> </span><span class="nx">ageP</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">flag</span><span class="p">.</span><span class="nx">Int</span><span class="p">(</span><span class="s">&quot;age&quot;</span><span class="p">,</span><span class="w"> </span><span class="mi">18</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;年龄&quot;</span><span class="p">)</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">flag</span><span class="p">.</span><span class="nx">Parse</span><span class="p">()</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;%T %[1]v\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">name</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;%T %[1]v\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">ageP</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;%T %[1]v\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="o">*</span><span class="nx">ageP</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>只要在 flag.Parse() 执行前覆盖掉 flag.Usage 即可。<br>
下面那行 flag.PrintDefaults() 则是打印帮助信息中其他的内容。完全可以把这行去掉，这里完全可以自定义打印更多其他内容，甚至是执行其他操作。  </p>
<h4 id="ding-zhi-commandline"><a class="toclink" href="#ding-zhi-commandline">定制 CommandLine</a></h4>
<p>在调用flag包中的一些函数（比如StringVar、Parse等等）的时候，实际上是在调用flag.CommandLine变量的对应方法。<br>
flag.CommandLine相当于默认情况下的命令参数容器。通过对flag.CommandLine重新赋值，就可以更深层次地定制当前命令源码文件的参数使用说明。<br>
flag包提供了NewFlagSet函数用于创建自定制的 CommandLine 。在上一个简单例子的基础上，修改一下其中的init函数的内容：</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span><span class="w"> </span><span class="nx">name</span><span class="w"> </span><span class="kt">string</span><span class="w"></span>
<span class="kd">var</span><span class="w"> </span><span class="nx">age</span><span class="w"> </span><span class="kt">int</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">init</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">flag</span><span class="p">.</span><span class="nx">CommandLine</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">flag</span><span class="p">.</span><span class="nx">NewFlagSet</span><span class="p">(</span><span class="s">&quot;&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">flag</span><span class="p">.</span><span class="nx">ExitOnError</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">flag</span><span class="p">.</span><span class="nx">StringVar</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">name</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;name&quot;</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;Adam&quot;</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;名字&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">age</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="o">*</span><span class="nx">flag</span><span class="p">.</span><span class="nx">Int</span><span class="p">(</span><span class="s">&quot;age&quot;</span><span class="p">,</span><span class="w"> </span><span class="mi">18</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;年龄&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="c1">// 和上面两句效果一样</span><span class="w"></span>
<span class="w">    </span><span class="c1">// flag.CommandLine.StringVar(&amp;name, &quot;name&quot;, &quot;Adam&quot;, &quot;名字&quot;)</span><span class="w"></span>
<span class="w">    </span><span class="c1">// var ageP = flag.CommandLine.Int(&quot;age&quot;, 18, &quot;年龄&quot;)</span><span class="w"></span>
<span class="w">    </span><span class="nx">flag</span><span class="p">.</span><span class="nx">CommandLine</span><span class="p">.</span><span class="nx">Usage</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="kd">func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Fprintln</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;请指定名字和年龄：&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="nx">flag</span><span class="p">.</span><span class="nx">PrintDefaults</span><span class="p">()</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>其实这里只加了一行语句。所有flag包的操作都要在flag.NewFlagSet执行之后，否则之前执行的内容会被覆盖掉。所以这里把flag.Int的调用移到了包内，否则在全局中的赋值语句会在这之前就运行了，然后被flag.NewFlagSet方法覆盖掉。<br>
这里无论是 <code>flag.StringVar</code> 或者是 <code>flag.CommandLine.StringVar</code>，最终都是使用flag.NewFlagSet创建的 *FlagSet 对象的方法来调用的。不过本质上是有差别的：</p>
<ul>
<li><code>flag.StringVar</code>: 使用默认 CommandLine 的对象调用，但是第一行语句 <code>flag.CommandLine = flag.NewFlagSet("", flag.ExitOnError)</code> 则是把它的值覆盖为新创建的对象。</li>
<li><code>flag.CommandLine.StringVar</code>: 使用 flag.NewFlagSet 函数创建的对象来调用，所以和上面是一个东西。</li>
</ul>
<p>第一个方式是专门为默认的容器提供的便捷调用方式。第二个是则是通用的方法，之后创建私有命令参数容器的时候就需要用通用的方式来调用了。<br>
Usage 必须用flag.CommandLine调用。另外不定制的话，包里也准备了默认的方法可以使用：</p>
<div class="highlight"><pre><span></span><code><span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">f</span><span class="w"> </span><span class="o">*</span><span class="nx">FlagSet</span><span class="p">)</span><span class="w"> </span><span class="nx">defaultUsage</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">f</span><span class="p">.</span><span class="nx">name</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s">&quot;&quot;</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Fprintf</span><span class="p">(</span><span class="nx">f</span><span class="p">.</span><span class="nx">Output</span><span class="p">(),</span><span class="w"> </span><span class="s">&quot;Usage:\n&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Fprintf</span><span class="p">(</span><span class="nx">f</span><span class="p">.</span><span class="nx">Output</span><span class="p">(),</span><span class="w"> </span><span class="s">&quot;Usage of %s:\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">f</span><span class="p">.</span><span class="nx">name</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="nx">f</span><span class="p">.</span><span class="nx">PrintDefaults</span><span class="p">()</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>第一个参数的作用基本就是显示一个名称，也可以用空字符串，向上面这样。而第二个参数可以是下面三种常量：</p>
<div class="highlight"><pre><span></span><code><span class="kd">const</span><span class="w"> </span><span class="p">(</span><span class="w"></span>
<span class="w">    </span><span class="nx">ContinueOnError</span><span class="w"> </span><span class="nx">ErrorHandling</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="kc">iota</span><span class="w"> </span><span class="c1">// Return a descriptive error.</span><span class="w"></span>
<span class="w">    </span><span class="nx">ExitOnError</span><span class="w">                          </span><span class="c1">// Call os.Exit(2).</span><span class="w"></span>
<span class="w">    </span><span class="nx">PanicOnError</span><span class="w">                         </span><span class="c1">// Call panic with a descriptive error.</span><span class="w"></span>
<span class="p">)</span><span class="w"></span>
</code></pre></div>

<p>效果一看就明白了。定义在解析遇到问题后，是执行何种操作。默认的就是ExitOnError，所以在--help执行打印说明后，最后一行会出现“exit status 2”，以状态码2退出。这里可以根据需要定制为抛出Panic。<br>
使用-h参数打印帮助信息也算是解析出错，如果是Panic则会在打印帮助信息后Panic，如果是Continue则先打印帮助信息然后按照默认值执行。所以如果要使用另外两种模式，最好修改一下-h参数的行为，就是上面讲的定制Usage。使用-h参数之后程序将执行的就是Usage指定的函数。  </p>
<h4 id="chuang-jian-si-you-ming-ling-can-shu-rong-qi"><a class="toclink" href="#chuang-jian-si-you-ming-ling-can-shu-rong-qi">创建私有命令参数容器</a></h4>
<p>上一个例子依然是使用flag包提供的命令参数容器，只是重新进行了创建和赋值。这里依然是调用flag.NewFlagSet()函数创建命令参数容器。<br>
这次是赋值给自定义的变量：</p>
<div class="highlight"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w"></span>

<span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;flag&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;fmt&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;os&quot;</span><span class="w"></span>
<span class="p">)</span><span class="w"></span>

<span class="kd">var</span><span class="w"> </span><span class="nx">cmdLine</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">flag</span><span class="p">.</span><span class="nx">NewFlagSet</span><span class="p">(</span><span class="s">&quot;&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">flag</span><span class="p">.</span><span class="nx">ExitOnError</span><span class="p">)</span><span class="w"></span>
<span class="kd">var</span><span class="w"> </span><span class="nx">name</span><span class="w"> </span><span class="kt">string</span><span class="w"></span>
<span class="kd">var</span><span class="w"> </span><span class="nx">age</span><span class="w"> </span><span class="kt">int</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">init</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">cmdLine</span><span class="p">.</span><span class="nx">StringVar</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">name</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;name&quot;</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;Adam&quot;</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;名字&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">age</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="o">*</span><span class="nx">cmdLine</span><span class="p">.</span><span class="nx">Int</span><span class="p">(</span><span class="s">&quot;age&quot;</span><span class="p">,</span><span class="w"> </span><span class="mi">18</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;年龄&quot;</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">cmdLine</span><span class="p">.</span><span class="nx">Parse</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Args</span><span class="p">[</span><span class="mi">1</span><span class="p">:])</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;%T %[1]v\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">name</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;%T %[1]v\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">age</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>首先通过 flag.NewFlagSet 函数创建了私有的命令参数容器。然后调用其他方法的接收者都使用这个容器。另外还有很多方法可以调用，可以继续探索。  </p>
      </div>
      <div class="back-to-top">
        <a href="/">HOME</a>
        <a href="#top">TOP</a>
      </div>
    </article>
  </div>
<!-- end article -->
<!-- 页面往下滚动一段之后才会显示TOC -->
<script>
  window.onscroll = function() {
    var tocbox = document.getElementsByClassName('toc')[0];
    var osTop = document.documentElement.scrollTop || document.body.scrollTop;
    var osWidth = document.documentElement.scrollWidth || document.body.scrollWidth;
    // console.log(osTop)
    if (osTop>300 && osWidth>865) {
      tocbox.style.display = "block"
    }
    if (osTop<300 || osWidth<865) {
      tocbox.style.display = "none"
    }
  }
</script>
                <footer>
                    <div class="icons">
                    </div>
                    <span id="busuanzi_container_page_pv" style="padding: 10px">本文阅读量<span id="busuanzi_value_page_pv"></span>次</span>
                    <span id="busuanzi_container_site_pv" style="padding: 10px">本站总访问量<span id="busuanzi_value_site_pv"></span>次</span>
                    <span id="busuanzi_container_site_uv" style="padding: 10px">本站总访客数<span id="busuanzi_value_site_uv"></span>人</span>
                    <p>© <script>document.write(moment().format('YYYY'));</script> 749B</p>
                </footer>
        </div>
</body>
</html>